如果一個智能合約至少有一個未實現的函數,即某個函數缺少主體 {} 中的內容,則必須將該合約標為 abstract,不然編譯會報錯。另外,未實現的函數需要加 virtual,以便子合約重寫。如果我們還沒想好具體怎麼實現函數,那麼可以把合約標為 abstract,之後讓別人補寫上。例如插入排序函數可以先用 abstract 標示:
abstract contract InsertSort{
function insertSort(uint[] memory a) public pure virtual returns(uint[] memory);
}
抽象合約範例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
abstract contract Base{
string public name = "Base";
function getAlias() public pure virtual returns(string memory);
}
contract BaseImpl is Base {
function getAlias() public pure override returns(string memory){
return "BaseTmpl";
}
}

抽象合約是不能部署的,要選擇非抽象合約部署。
介面類似於抽象合約,但它不實現任何功能。
external 且不能有函數本體 {}
如果智能合約實現了某種介面(例如ERC20或ERC721),其他 Dapps 和智能合約就知道如何與它互動。因為介面提供了兩個重要的資訊:
bytes4 選擇器,以及函數簽章 <函數名稱>(每個參數型別)。ABI json 檔轉換為介面 sol 檔。以 ERC721 介面合約 IERC721 為例,它定義了 3 個 event 和 9 個 function,所有 ERC721 標準的 NFT 都實作了這些函數。介面和常規合約的區別在於每個函數都以 ; 代替函數本體 { } 結尾。
interface IERC721 is IERC165{
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function TransferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
Transfer 事件:在轉帳時被釋放,記錄代幣的發出地址 from ,接收地址 to 和 tokenId。Approval 事件:在授權時被釋放,記錄授權地址 owner,被授權地址 approved 和 tokenId。ApprovalForAll 事件:在批量授權時被釋放,記錄批量授權的發出地址 owner,被授權地址 operator 和是否授權的 approved。balanceOf:傳回某地址的 NFT 持有量 balance。ownerOf:回傳某 tokenId 的主人 owner。transferFrom:普通轉賬,參數為轉出地址 from,接收地址 to 和 tokenId。safeTransferFrom:安全轉帳(如果接收方是合約位址,會要求實作 ERC721Receiver 介面)。approve:授權另一個位址使用你的 NFT。參數為被授權位址 approve 和 tokenId。getApproved:查詢 tokenId 被批准給了哪個位址。setApprovedForAll:將自己持有的該系列 NFT 批次授權給某個地址 operator。isApprovedForAll:查詢某位址的 NFT 是否大量授權給了另一個 operator 位址。safeTransferFrom:安全轉帳的重載函數,參數裡包含了 data。如果我們知道一個合約實現了 IERC721 介面,我們不需要知道它具體程式碼實現,就可以與它互動。
無聊猿 BAYC 屬於 ERC721 代幣,實現了 IERC721 介面的功能。我們不需要知道它的原始碼,只要知道它的合約地址,用 IERC721 介面就可以與它互動,例如用 balanceOf() 來查詢某個地址的 BAYC 餘額,用 safeTransferFrom() 來轉帳 BAYC。
contract interactBAYC{
// 利用BAYC位址建立介面合約變數(在 ETH 主網)
IERC721 BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D);
// 透過介面呼叫 BAYC 的 balanceOf() 查詢持倉量
function balanceOfBAYC(address owner) external view returns (uint256 balance){
return BAYC.balanceOf(owner);
}
// 透過介面呼叫 BAYC 的 safeTransferFrom() 安全轉賬
function safeTransferFromBAYC(address from, address to, uint256 tokenId) external {
BAYC.safeTransferFrom(from, to, tokenId);
}
}
介面範例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface Base{
function getFirstName() external pure returns(string memory);
function getLastName() external pure returns(string memory);
}
contract BaseImpl is Base{
function getFirstName() external pure override returns(string memory){
return "Amazing";
}
function getLastName() external pure override returns(string memory){
return "Ang";
}
}
